/*

 * HTML (Base Structure)
<div id="master"> <!-- DOM element for ItemsList -->
    <div class="noitem"></div> <!-- this is not an item -->
    <div>
        <form>
        <input type="hidden" name="id" value="1"> <!-- this is the item's ID -->
        <input ...>
        <select ...> <!-- these field will be submitted -->
        <button type="button" class="action-create">Create</button> <!-- this button for create (customizable class name) -->
        <button type="button" class="action-update">Update</button> <!-- this button for update -->
        <button type="button" class="action-delete">Delete</button> <!-- this button for delete -->
        </form>
    </div> <!-- this is an item -->
</div>

 * JAVASCRIPT (Base Structure)
 var itemlist = new ItemsList('name',$('master'),{
                                                 options...
                                                 });

 */
var ItemsList = new Class({
    Implements: [Options,Events],

    name: '',
    itemElement:null, //relative DOM element
    transient: false, //list transient

    options: { classes:     { 'list':          'itemslist',
                              'item':          'item',
                              'action-add':    'action-add',
                              'action-update': 'action-update',
                              'action-create': 'action-create',
                              'action-delete': 'action-delete',
                              'action-read':   'action-read'},
               highlight:   false, //element
               debug:       false,
               animate:     true,
               container:   true,
               async:       true,
               url_params:  { }, // substitution in url
               url_args:    { }, // passed as data with form input data
               url_methods: { 'create': '',
                              'read':   '',
                              'update': '',
                              'delete': ''},
               restmethods: false,
               template:    function() { return new Element('div'); }
             },

    validator: false, // Form.Validator instance (will call .validate() or NONE on submit)

    items: Array(), // items collection

    highlightElement: null,

    initialize: function(name, itemElement, options){
        this.setOptions(options);

        this.name = name;
        this.itemElement = itemElement;
        this.itemElement.addClass(this.options.classes['list']);
        itemElement.instance = this;

        this.highlightElement = this.options.highlight;

        this.validator = options.validator;

        if (this.options.container) {
            var act = this.itemElement.getElement('.'+this.options.classes['action-add']);
            if (act)  act.addEvent('click',function() { this.add(); }.bind(this));
        }

        this.addEvent('createsuccess', function(item,data) { item.postCreate(data); });
        this.addEvent('readsuccess',   function(item,data) { item.postRead(data); });
        this.addEvent('updatesuccess', function(item,data) { item.postUpdate(data); });
        this.addEvent('deletesuccess', function(item,data) { item.postDelete(data); });
        this.addEvent('failure',       function(item,data) { item.postFailure(data); });

        this.start();
    },

    submitall: function() {
        Array.forEach(this.items, function(el) {
            if (el.transient) el.submit();
        });
    },

    getid: function() {
        if (this.items.length==1) return this.items[0].id;
        if (this.options.debug) console.log('no id [zero items or multiple items]');
        return null;
    },

    start: function(){
        this.itemElement.getChildren('div:not(.notitem)').forEach(function(el) {
            this.add(el,"append");
        }.bind(this));
    },

    add: function(attachElement,append) {
        if (!this.options.container) return;

        //make template -> form
        var newElement = '';
        var transient = true;
        if (attachElement) {
            if (append) transient = false;
            newElement = attachElement;
        } else {
            newElement = this.options.template();
        }

        if (typeof(newElement)=='string') {
            newElement = newElement.toElement()[0];
        } else if (!append) {
            newElement = Element.clone(newElement);
            newElement.show();
            newElement.removeProperty('id');
        }

        newElement.addClass(this.options.classes['item']);
        newElement.inject(this.itemElement);

        var cls = new Item(this,newElement);
        cls.setTransient(transient);
        this.items.push(cls);

        if (this.options.animate && !attachElement) {
            newElement.slide('hide');
            this.getHighlight(newElement).highlight('#62951C');
            newElement.slide('in');
        }
    },
    
    setTransient: function() {
        Array.forEach(this.items, function(el) {
            el.setTransient(true);
        });
    },

    remove: function(item) {
        if (!this.options.container) return;
        this.items.erase(item);
        item=null;
    },

    getHighlight: function(item) {
        if (this.options.debug) console.log(this.highlightElement);
        return (this.highlightElement ? this.highlightElement : item);
    }

});

var Item = new Class({
    Implements: Events,

    itemElement:null,
    formElement:null,
    id:null, //my id
    itemsList:null, //class container

    transient:false,

    /**********/
    
    initialize:function(itemsList,itemElement) {
        itemElement.instance = this;
        this.itemElement = itemElement;
        this.formElement = this.itemElement.getElement('form');

        this.itemsList = itemsList;

        /**/
        /* GET ID */

        var itemId = this.itemElement.getElement('input[name=id]');
        if (itemId) {
            this.setId(itemId.value || 0);
            itemId.destroy();
        } else {
            this.setId(0);
        }

        /**/
        /* SET [SUBMIT] EVENT ON FORM */

        var form = this.itemElement.getElement('form');
        if (form) {
            form.addEvent('submit',function() { this.submit(); return false; }.bind(this));
        }

        /***/
        /* BUTTON EVENTS */

        var act = this.itemElement.getElement('.'+this.itemsList.options.classes['action-create']);
        if (act) {
            act.addEvent('click',function() { this.create(); }.bind(this));
            if (this.id) act.destroy();
        }

        var act = this.itemElement.getElement('.'+this.itemsList.options.classes['action-read']);
        if (act) {
            act.addEvent('click',function() { this.read(); }.bind(this));
            if (!this.id) act.hide();
        }
        var act = this.itemElement.getElement('.'+this.itemsList.options.classes['action-update']);
        if (act) {
            act.addEvent('click',function() { this.update(); }.bind(this));
            if (!this.id) act.hide();
        }

        var act = this.itemElement.getElement('.'+this.itemsList.options.classes['action-delete']);
        if (act) act.addEvent('click',function() { 
           this.delete();
        }.bind(this));

        /***/
        if (this.itemsList.options.debug) console.debug('> ITEM INITIALIZE, '+this.id);
        /***/

        this.itemsList.fireEvent('initializeitem',[this]);
    },

    /**********/

    setId: function(id) {
        if (this.id==0) {
            var act = this.itemElement.getElement('.action-create');
            if (act) act.destroy();
            var act = this.itemElement.getElement('.action-update');
            if (act) act.show();
        }
        this.id = parseInt(id);
        this.itemElement.setAttribute('item:id',id);
    }.protect(),

    setTransient: function(transient) {
        if (this.transient == transient) return;
        this.transient = transient;
        this.itemsList.transient = transient;
        this.itemsList.fireEvent('statuschange',[this]);
    },

    /**********/

    submit: function() { // execute create or update wisely
        if (this.id==0) this.create();
        else this.update();
    },

    /**********/

    getdata: function(excludeform) {
        data = {}
        if (!excludeform) data = Object.merge(data,this.formElement.getFormItems());
        data = Object.merge(data,this.itemsList.options.url_args);
        data.id = this.id;
        if (this.itemsList.options.debug) console.debug(data);
        return data;
    }.protect(),

    validate: function() {
        if (!this.itemsList.validator) return true;
        isvalid = this.itemsList.validator.validate();
        return isvalid;
    }.protect(),

    create: function() {
        var url = this.getUrl('create');
        if (this.id!=0 || !url || !this.validate()) return;

        if (this.itemsList.options.debug) console.log('> ACTION create');
        this.execute('createsuccess', url,'POST', this.getdata());
    },

    read: function() {
        var url = this.getUrl('read');
        if (!url) return;
        if (this.itemsList.options.debug) console.log('> ACTION read');
        this.execute('readsuccess', url,'GET', this.getdata("noform"));
    },

    update: function() {
        var url = this.getUrl('update');
        if (this.id==0 || !url || !this.validate()) return;
        if (this.itemsList.options.debug) console.log('> ACTION update');
        this.execute('updatesuccess', url,this.itemsList.options.restmethods?'PUT':'POST', this.getdata());
    },

    delete: function() {
        var url = this.getUrl('delete');
        if (this.id==0) {
            this.itemsList.fireEvent('deletesuccess',this);
            return;
        }
        if (!url) return;
        if (this.itemsList.options.debug) console.log('> ACTION delete');
        this.execute('deletesuccess', url,this.itemsList.options.restmethods?'DELETE':'POST', this.getdata("noform"));
    },

    /**********/

    postCreate: function(data) {
        this.setId(data['id']);
        this.setTransient(false);
        if (this.itemsList.options.animate) {
            this.itemsList.getHighlight(this.itemElement).highlight('#62951C');
        }
    },

    postRead: function(data) {
        // this.formElement.setFormItems(data);
    },

    postUpdate: function(data) {
        this.setTransient(false);
        if (this.itemsList.options.animate) {
            this.itemsList.getHighlight(this.itemElement).highlight('#62951C');
        }
    },

    postDelete: function(data) {
        var destroy = function() {
            this.itemElement.destroy();
            this.itemsList.remove(this);
            this.itemsList.fireEvent('postdelete');
        }.bind(this);

        if (this.itemsList.options.animate) {
            if (this.itemsList.options.debug) console.log('delete');
            this.itemsList.getHighlight(this.itemElement).highlight('#C8171D');
            this.itemElement.get('slide').chain(function() { destroy(); });
            this.itemElement.slide('out');
        } else {
            destroy();
        }
    },

    postFailure: function(data) {
        if (this.itemsList.options.animate) {
            this.itemsList.getHighlight(this.itemElement).highlight('#C8171D');
        }
    },

    /**********/

    execute: function(onsuccess, url,method,data) {
        if (this.itemsList.options.debug) console.log('> EXECUTE');
        if (this.itemsList.options.debug) console.log('> URL: ' + url);
        new Request.JSON({'url':url,
                          'method':method,
                          'async':this.itemsList.options.async,
                          'data':data,
                          onSuccess:function(json) {
                              if (json.hasOwnProperty("error")) {
                                  this.itemsList.fireEvent('failure',[this,json]);
                              } else {
                                  if (onsuccess) this.itemsList.fireEvent(onsuccess,[this,json]);
                              }
                          }.bind(this),
                          onFailure:function(xhr) {
                              if (this.itemsList.options.debug) console.debug(xhr);
                              var json = {"error":{'description':'HTTP Error '+xhr.status + ': '+xhr.statusText}};
                              try {
                              json = JSON.parse(xhr.responseText);
                              } catch(e) {}
                              this.itemsList.fireEvent('failure',[this,json]);
                          }.bind(this)
                          }).send();
    }.protect(),

    getUrl:function(method) {
        var params = this.itemsList.options.url_params;
        params.id = this.id;
        return this.itemsList.options.url_methods[method].substitute(params);
    }.protect(),

    /*****************/
    
});

